From 54d4930f44a10824346217f8a499370f3ef12445 Mon Sep 17 00:00:00 2001 From: Jyrki Gadinger Date: Thu, 6 Mar 2025 09:17:40 +0100 Subject: [PATCH] make use of the new `x-nc-skip-trashbin` header Signed-off-by: Jyrki Gadinger --- .../basepropagateremotedeleteencrypted.cpp | 1 + src/libsync/deletejob.cpp | 14 +++++++++++ src/libsync/deletejob.h | 4 ++++ src/libsync/discovery.cpp | 6 +++++ src/libsync/discoveryphase.cpp | 23 +++++++++++++++++++ src/libsync/discoveryphase.h | 5 ++++ src/libsync/propagateremotedelete.cpp | 3 ++- ...opagateremotedeleteencryptedrootfolder.cpp | 1 + src/libsync/syncfileitem.h | 4 ++++ 9 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/libsync/basepropagateremotedeleteencrypted.cpp b/src/libsync/basepropagateremotedeleteencrypted.cpp index 05c0ad9ed..17e5d95aa 100644 --- a/src/libsync/basepropagateremotedeleteencrypted.cpp +++ b/src/libsync/basepropagateremotedeleteencrypted.cpp @@ -154,6 +154,7 @@ void BasePropagateRemoteDeleteEncrypted::deleteRemoteItem(const QString &filenam qCInfo(ABSTRACT_PROPAGATE_REMOVE_ENCRYPTED) << "Deleting nested encrypted item" << filename; const auto deleteJob = new DeleteJob(_propagator->account(), _propagator->fullRemotePath(filename), {}, this); + deleteJob->setSkipTrashbin(true); if (_encryptedFolderMetadataHandler && _encryptedFolderMetadataHandler->folderMetadata() && _encryptedFolderMetadataHandler->folderMetadata()->isValid()) { deleteJob->setFolderToken(_encryptedFolderMetadataHandler->folderToken()); diff --git a/src/libsync/deletejob.cpp b/src/libsync/deletejob.cpp index f81c83c87..67a79756d 100644 --- a/src/libsync/deletejob.cpp +++ b/src/libsync/deletejob.cpp @@ -44,6 +44,10 @@ void DeleteJob::start() req.setRawHeader(oneHeaderIt.key(), oneHeaderIt.value()); } + if (_skipTrashbin) { + req.setRawHeader("X-NC-Skip-Trashbin", "true"); + } + if (_url.isValid()) { startRequest("DELETE", _url, req); } else { @@ -61,4 +65,14 @@ void DeleteJob::setFolderToken(const QByteArray &folderToken) _folderToken = folderToken; } +bool DeleteJob::skipTrashbin() const +{ + return _skipTrashbin; +} + +void DeleteJob::setSkipTrashbin(bool skipTrashbin) +{ + _skipTrashbin = skipTrashbin; +} + } // namespace OCC diff --git a/src/libsync/deletejob.h b/src/libsync/deletejob.h index d244e5a85..b796556e0 100644 --- a/src/libsync/deletejob.h +++ b/src/libsync/deletejob.h @@ -35,9 +35,13 @@ public: [[nodiscard]] QByteArray folderToken() const; void setFolderToken(const QByteArray &folderToken); + [[nodiscard]] bool skipTrashbin() const; + void setSkipTrashbin(bool skipTrashbin); + private: QMap _headers = {}; QUrl _url; // Only used if the constructor taking a url is taken. QByteArray _folderToken; + bool _skipTrashbin = false; }; } diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index 6bb6b75e2..1ada8a5b5 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -1433,6 +1433,11 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo( const bool isOnlineOnlyItem = isCfApiVfsMode && (localEntry.isDirectory || _discoveryData->_syncOptions._vfs->isDehydratedPlaceholder(_discoveryData->_localDir + path._local)); const auto isE2eeMoveOnlineOnlyItemWithCfApi = isE2eeMove && isOnlineOnlyItem; + if (isE2eeMove) { + qCInfo(lcDisco) << "requesting permanent deletion for" << originalPath; + _discoveryData->_permanentDeletionRequests.insert(originalPath); + } + if (isE2eeMoveOnlineOnlyItemWithCfApi) { item->_instruction = CSYNC_INSTRUCTION_NEW; item->_direction = SyncFileItem::Down; @@ -1784,6 +1789,7 @@ void ProcessDirectoryJob::processFileFinalize( job->setInsideEncryptedTree(isInsideEncryptedTree() || item->isEncrypted()); if (removed) { job->setParent(_discoveryData); + _discoveryData->_deletedItem[path._original] = item; _discoveryData->enqueueDirectoryToDelete(path._original, job); } else { connect(job, &ProcessDirectoryJob::finished, this, &ProcessDirectoryJob::subJobFinished); diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index 0114127ff..469dec380 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -225,6 +225,28 @@ void DiscoveryPhase::enqueueDirectoryToDelete(const QString &path, ProcessDirect } } +void DiscoveryPhase::markPermanentDeletionRequests() +{ + // since we don't know in advance which files/directories need to be permanently deleted, + // we have to look through all of them at the end of the run + for (const auto &originalPath : _permanentDeletionRequests) { + const auto it = _deletedItem.find(originalPath); + if (it == _deletedItem.end()) { + qCWarning(lcDiscovery) << "didn't find an item for" << originalPath << "(yet)"; + continue; + } + + auto item = *it; + if (!(item->_instruction == CSYNC_INSTRUCTION_REMOVE || item->_direction == SyncFileItem::Up)) { + qCWarning(lcDiscovery) << "will not request permanent deletion for" << originalPath << "as the instruction is not CSYNC_INSTRUCTION_REMOVE, or the direction is not Up"; + continue; + } + + qCInfo(lcDiscovery) << "requested permanent server-side deletion for" << originalPath; + item->_wantsPermanentDeletion = true; + } +} + void DiscoveryPhase::startJob(ProcessDirectoryJob *job) { ENFORCE(!_currentRootJob); @@ -242,6 +264,7 @@ void DiscoveryPhase::startJob(ProcessDirectoryJob *job) auto nextJob = _queuedDeletedDirectories.take(_queuedDeletedDirectories.firstKey()); startJob(nextJob); } else { + markPermanentDeletionRequests(); emit finished(); } }); diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h index 855758b1f..0c9edceac 100644 --- a/src/libsync/discoveryphase.h +++ b/src/libsync/discoveryphase.h @@ -315,6 +315,11 @@ class DiscoveryPhase : public QObject void enqueueDirectoryToDelete(const QString &path, ProcessDirectoryJob* const directoryJob); + /// contains files/folder names that are requested to be deleted permanently + QSet _permanentDeletionRequests; + + void markPermanentDeletionRequests(); + public: // input QString _localDir; // absolute path to the local directory. ends with '/' diff --git a/src/libsync/propagateremotedelete.cpp b/src/libsync/propagateremotedelete.cpp index b37636607..655739495 100644 --- a/src/libsync/propagateremotedelete.cpp +++ b/src/libsync/propagateremotedelete.cpp @@ -69,13 +69,14 @@ void PropagateRemoteDelete::createDeleteJob(const QString &filename) } } - qCInfo(lcPropagateRemoteDelete) << "Deleting file, local" << _item->_file << "remote" << remoteFilename; + qCInfo(lcPropagateRemoteDelete) << "Deleting file, local" << _item->_file << "remote" << remoteFilename << "wantsPermanentDeletion" << _item->_wantsPermanentDeletion; auto headers = QMap{}; if (_item->_locked == SyncFileItem::LockStatus::LockedItem) { headers[QByteArrayLiteral("If")] = (QLatin1String("<") + propagator()->account()->davUrl().toString() + _item->_file + "> (_lockToken.toUtf8() + ">)").toUtf8(); } _job = new DeleteJob(propagator()->account(), propagator()->fullRemotePath(remoteFilename), headers, this); + _job->setSkipTrashbin(_item->_wantsPermanentDeletion); connect(_job.data(), &DeleteJob::finishedSignal, this, &PropagateRemoteDelete::slotDeleteJobFinished); propagator()->_activeJobList.append(this); _job->start(); diff --git a/src/libsync/propagateremotedeleteencryptedrootfolder.cpp b/src/libsync/propagateremotedeleteencryptedrootfolder.cpp index 55dd4910f..513c69d89 100644 --- a/src/libsync/propagateremotedeleteencryptedrootfolder.cpp +++ b/src/libsync/propagateremotedeleteencryptedrootfolder.cpp @@ -184,6 +184,7 @@ void PropagateRemoteDeleteEncryptedRootFolder::deleteNestedRemoteItem(const QStr auto deleteJob = new DeleteJob(_propagator->account(), _propagator->fullRemotePath(filename), {}, this); deleteJob->setFolderToken(folderToken()); deleteJob->setProperty(encryptedFileNamePropertyKey, filename); + deleteJob->setSkipTrashbin(true); connect(deleteJob, &DeleteJob::finishedSignal, this, &PropagateRemoteDeleteEncryptedRootFolder::slotDeleteNestedRemoteItemFinished); diff --git a/src/libsync/syncfileitem.h b/src/libsync/syncfileitem.h index 71dc97387..154d13a74 100644 --- a/src/libsync/syncfileitem.h +++ b/src/libsync/syncfileitem.h @@ -347,6 +347,10 @@ public: bool isPermissionsInvalid = false; QString _discoveryResult; + + /// if true, requests the file to be permanently deleted instead of moved to the trashbin + /// only relevant for when `_instruction` is set to `CSYNC_INSTRUCTION_REMOVE` + bool _wantsPermanentDeletion = false; }; inline bool operator<(const SyncFileItemPtr &item1, const SyncFileItemPtr &item2) -- 2.30.2